"""数据库配置和会话管理模块。

该模块负责：
1. 配置异步和同步数据库引擎
2. 创建会话工厂
3. 提供数据库会话依赖
4. 管理数据库表的创建和删除

使用SQLAlchemy作为ORM，支持PostgreSQL + PostGIS + TimescaleDB。
"""

from collections.abc import AsyncGenerator, Generator

from sqlalchemy import create_engine
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import Session, sessionmaker

from ..core.config import settings
from ..core.telemetry import instrument_database
from ..domain.models import Base

# 异步引擎用于FastAPI应用
async_engine = create_async_engine(
    settings.database.url,
    echo=settings.wms.debug,
    pool_size=20,
    max_overflow=30,
    pool_pre_ping=True,
)

# 同步引擎用于迁移和工具
sync_engine = create_engine(
    settings.database.sync_url,
    echo=settings.wms.debug,
    pool_size=10,
    max_overflow=20,
    pool_pre_ping=True,
)

# 仪器化数据库引擎
instrument_database(async_engine.sync_engine)  # Instrument the underlying sync engine
instrument_database(sync_engine)

# 会话工厂
AsyncSessionLocal = async_sessionmaker(
    bind=async_engine,
    class_=AsyncSession,
    expire_on_commit=False,
)

SessionLocal = sessionmaker(
    bind=sync_engine,
    expire_on_commit=False,
)


def get_db() -> Generator[Session, None, None]:
    """获取数据库会话（同步）。

    用于需要同步数据库操作的场景，如迁移脚本。

    Yields:
        Session: 数据库会话对象
    """
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


async def get_async_db() -> AsyncGenerator[AsyncSession, None]:
    """获取异步数据库会话。

    用于FastAPI的依赖注入，提供异步数据库会话。

    Yields:
        AsyncSession: 异步数据库会话对象
    """
    async with AsyncSessionLocal() as session:
        try:
            yield session
        finally:
            await session.close()


async def create_tables() -> None:
    """创建数据库表。

    根据SQLAlchemy模型定义创建所有数据库表。
    """
    async with async_engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)


async def drop_tables():
    """删除数据库表。

    删除所有数据库表，用于测试或重置环境。
    """
    async with async_engine.begin() as conn:
        await conn.run_sync(Base.metadata.drop_all)
